"""
图片处理工具类
提供图片编解码、质量检测等功能
"""
import base64
import io
from typing import Optional, Tuple
import cv2
import numpy as np
from PIL import Image
from utils.logger import app_logger


class ImageUtils:
    """图片处理工具类"""

    @staticmethod
    def bytes_to_cv2(img_bytes: bytes) -> Optional[np.ndarray]:
        """
        字节流转OpenCV图像

        Args:
            img_bytes: 图片字节流

        Returns:
            OpenCV图像数组，失败返回None
        """
        try:
            img_array = np.frombuffer(img_bytes, dtype=np.uint8)
            img = cv2.imdecode(img_array, cv2.IMREAD_COLOR)

            if img is None:
                app_logger.error("字节流解码失败，无法转换为图像")
                return None

            return img

        except Exception as e:
            app_logger.error(f"字节流转图像失败: {e}", exc_info=True)
            return None

    @staticmethod
    def base64_to_cv2(base64_str: str) -> Optional[np.ndarray]:
        """
        Base64字符串转OpenCV图像

        Args:
            base64_str: base64编码的图片字符串

        Returns:
            OpenCV图像数组，失败返回None
        """
        try:
            # 移除可能的data:image前缀
            if ',' in base64_str:
                base64_str = base64_str.split(',')[1]

            # 解码base64
            img_data = base64.b64decode(base64_str)
            img_array = np.frombuffer(img_data, dtype=np.uint8)
            img = cv2.imdecode(img_array, cv2.IMREAD_COLOR)

            if img is None:
                app_logger.error("Base64解码失败，无法转换为图像")
                return None

            return img

        except Exception as e:
            app_logger.error(f"Base64转图像失败: {e}", exc_info=True)
            return None

    @staticmethod
    def cv2_to_base64(img: np.ndarray, format: str = ".jpg") -> Optional[str]:
        """
        OpenCV图像转Base64字符串

        Args:
            img: OpenCV图像数组
            format: 图片格式 (.jpg, .png等)

        Returns:
            base64字符串，失败返回None
        """
        try:
            _, buffer = cv2.imencode(format, img)
            img_bytes = buffer.tobytes()
            base64_str = base64.b64encode(img_bytes).decode('utf-8')
            return base64_str

        except Exception as e:
            app_logger.error(f"图像转Base64失败: {e}", exc_info=True)
            return None

    @staticmethod
    def pil_to_cv2(pil_img: Image.Image) -> np.ndarray:
        """PIL Image转OpenCV图像"""
        return cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR)

    @staticmethod
    def cv2_to_pil(cv2_img: np.ndarray) -> Image.Image:
        """OpenCV图像转PIL Image"""
        return Image.fromarray(cv2.cvtColor(cv2_img, cv2.COLOR_BGR2RGB))

    @staticmethod
    def calculate_sharpness(img: np.ndarray) -> float:
        """
        计算图像清晰度（使用拉普拉斯算子）

        Args:
            img: OpenCV图像

        Returns:
            清晰度分数，值越大越清晰
        """
        try:
            # 转灰度图
            if len(img.shape) == 3:
                gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
            else:
                gray = img

            # 计算拉普拉斯方差
            laplacian = cv2.Laplacian(gray, cv2.CV_64F)
            sharpness = laplacian.var()

            return float(sharpness)

        except Exception as e:
            app_logger.error(f"计算清晰度失败: {e}")
            return 0.0

    @staticmethod
    def check_brightness(img: np.ndarray) -> float:
        """
        检测图像亮度

        Args:
            img: OpenCV图像

        Returns:
            平均亮度值 0-255
        """
        try:
            # 转HSV，获取V通道
            if len(img.shape) == 3:
                hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
                brightness = np.mean(hsv[:, :, 2])
            else:
                brightness = np.mean(img)

            return float(brightness)

        except Exception as e:
            app_logger.error(f"检测亮度失败: {e}")
            return 0.0

    @staticmethod
    def resize_image(
        img: np.ndarray,
        target_size: Tuple[int, int],
        keep_aspect_ratio: bool = True
    ) -> np.ndarray:
        """
        调整图像大小

        Args:
            img: 原始图像
            target_size: 目标尺寸 (width, height)
            keep_aspect_ratio: 是否保持宽高比

        Returns:
            调整后的图像
        """
        try:
            if keep_aspect_ratio:
                h, w = img.shape[:2]
                target_w, target_h = target_size

                # 计算缩放比例
                scale = min(target_w / w, target_h / h)
                new_w, new_h = int(w * scale), int(h * scale)

                # 缩放
                resized = cv2.resize(img, (new_w, new_h), interpolation=cv2.INTER_AREA)

                # 创建目标画布并居中放置
                canvas = np.zeros((target_h, target_w, 3), dtype=np.uint8)
                y_offset = (target_h - new_h) // 2
                x_offset = (target_w - new_w) // 2
                canvas[y_offset:y_offset+new_h, x_offset:x_offset+new_w] = resized

                return canvas
            else:
                return cv2.resize(img, target_size, interpolation=cv2.INTER_AREA)

        except Exception as e:
            app_logger.error(f"调整图像大小失败: {e}")
            return img

    @staticmethod
    def validate_image(img: np.ndarray, min_size: int = 80) -> Tuple[bool, str]:
        """
        验证图像是否符合要求

        Args:
            img: 图像数组
            min_size: 最小尺寸要求

        Returns:
            (是否有效, 错误信息)
        """
        if img is None:
            return False, "图像为空"

        if len(img.shape) < 2:
            return False, "图像格式错误"

        h, w = img.shape[:2]
        if h < min_size or w < min_size:
            return False, f"图像尺寸过小，最小要求{min_size}x{min_size}"

        # 检查清晰度
        sharpness = ImageUtils.calculate_sharpness(img)
        if sharpness < 50:  # 阈值可调整
            return False, f"图像模糊，清晰度:{sharpness:.2f}"

        # 检查亮度
        brightness = ImageUtils.check_brightness(img)
        if brightness < 30 or brightness > 225:
            return False, f"图像亮度不合适，亮度值:{brightness:.2f}"

        return True, ""
